import math
import time

import logzero
import serial
import serial.tools.list_ports
from PyQt5.QtCore import QObject, Qt, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import (
    QComboBox,
    QDoubleSpinBox,
    QGridLayout,
    QGroupBox,
    QHBoxLayout,
    QHeaderView,
    QLabel,
    QMessageBox,
    QPlainTextEdit,
    QPushButton,
    QTableWidget,
    QTableWidgetItem,
    QWidget,
)

import mengy_spac800
import mengy_spac800_bt
from devices.mengy_ks833 import KS833_PACK_GEN, KS833_OutputStatus
from helpers.mengy_bytes_helper import bytes2Int, bytesPuttyPrint
from qt_custom_widget.qt_helper import addTooltipInSpinBox
from qt_custom_widget.qt_ReportBrowser import ReportBrowser, ReportBrowser_BT

logger = logzero.setup_logger("tabs.9.report")
BAUD_LIST = (1200, 2400, 4800, 9600, 19200, 38400, 56000, 57600, 115200)


class AutoTestWorker(QObject):

    sig_working = pyqtSignal(int, mengy_spac800.AutoTestUpdateConf, KS833_OutputStatus)  # 用于告知采集信息
    sig_ks833_status = pyqtSignal(int, KS833_OutputStatus)  # 用于提前告知功率源状态
    sig_finished = pyqtSignal(int)
    sig_output = pyqtSignal(str)

    def __init__(self, id: int, app, serialObj, rangeType, deviceModule, ks833Gen):
        super().__init__()
        self.__id = id
        self.__abort = False
        self.app = app
        self.serialObj = serialObj
        if rangeType == 0:
            self.output_list = mengy_spac800.AUTO_TEST_OUTPUT_MEA
        else:
            self.output_list = deviceModule.AUTO_TEST_OUTPUT_PRT
        self.rangeType = rangeType
        self.ks833Gen = ks833Gen

    def ser_write_pack(self, pack: bytes):
        try:
            if not self.serialObj.isOpen():
                self.serialObj.open()
            self.serialObj.write(pack)
        except serial.SerialException as e:
            self.sig_output.emit("端口故障 | {}".format(repr(e)))
            return False
        except serial.SerialTimeoutException as e:
            self.sig_output.emit("端口超时故障 | {}".format(repr(e)))
            return False
        except Exception as e:
            self.sig_output.emit("其他异常 | {}".format(repr(e)))
            return False
        else:
            recv_pack = self.serialObj.read(2048)
            if recv_pack:
                self.sig_output.emit("功率源回应 | {}".format(bytesPuttyPrint(recv_pack)))
            else:
                self.sig_output.emit("功率源无回应")
            return True
        finally:
            self.app.processEvents()

    def on_owari(self):
        """功率源停止输出并断开连接"""
        pack = self.ks833Gen.pack_shutdown_output()
        self.sig_output.emit("功率源停止输出 | {}".format(bytesPuttyPrint(pack)))
        self.ser_write_pack(pack)
        # pack = self.ks833Gen.pack_disconnect()
        # self.sig_output.emit("断开与功率源连接 | {}".format(bytesPuttyPrint(pack)))
        # self.ser_write_pack(pack)
        self.sig_output.emit("结束清理工作")
        self.sig_finished.emit(self.__id)

    def gen_output_send_pack(self, new_status: KS833_OutputStatus):
        if self.ks833Gen.is_switch_range(new_status):  # 需要重新切换档位
            pack = self.ks833Gen.pack_shutdown_output()  # 关闭输出
            self.ser_write_pack(pack)
            time.sleep(2)
            self.ks833Gen.conf_output_status(new_status)  # 重新配置输出值
            send_pack = self.ks833Gen.pack_start_output()  # 重新输出
        else:
            self.ks833Gen.conf_output_status(new_status)  # 重新配置输出值
            send_pack = self.ks833Gen.pack_adjust_output()  # 调整输出
        return send_pack

    @pyqtSlot()
    def work(self):
        thread_name = QThread.currentThread().objectName()
        thread_id = int(QThread.currentThreadId())
        self.sig_output.emit("启动进程 #{} from {}(#{})".format(self.__id, thread_name, thread_id))
        # connect ks833
        connect_pack = self.ks833Gen.pack_connect()
        self.sig_output.emit("开始与功率源建立连接 | {}".format(bytesPuttyPrint(connect_pack)))
        if self.__abort or (not self.ser_write_pack(connect_pack)):
            self.on_owari()  # 清理现场 终止输出
            return None
        # 开始输出
        atu_conf, ks833_status = self.output_list[0]
        self.ks833Gen.conf_output_status(ks833_status)
        send_pack = self.ks833Gen.pack_start_output()
        self.sig_output.emit("功率源启动输出 | {}\n{}".format(ks833_status, bytesPuttyPrint(send_pack)))
        if self.__abort or (not self.ser_write_pack(send_pack)):
            self.on_owari()  # 清理现场 终止输出
            return None
        self.sig_ks833_status.emit(self.__id, ks833_status)
        if atu_conf.pom == 0:
            time.sleep(15)  # 等待功率源输出稳定
        else:
            time.sleep(10)  # 等待功率源输出稳定
        self.sig_working.emit(self.__id, atu_conf, ks833_status)
        time.sleep(3)  # 等待数据采样完成

        # 调整输出
        for idx, data in enumerate(self.output_list[1:]):
            atu_conf, ks833_status = data
            send_pack = self.gen_output_send_pack(ks833_status)
            self.sig_output.emit("功率源输出 | {}\n{}".format(ks833_status, bytesPuttyPrint(send_pack)))
            if self.__abort or (not self.ser_write_pack(send_pack)):
                self.on_owari()  # 清理现场 终止输出
                return None
            self.sig_ks833_status.emit(self.__id, ks833_status)
            if atu_conf.pom == 0:
                time.sleep(15)  # 等待功率源输出稳定
            else:
                time.sleep(10)  # 等待功率源输出稳定
            self.sig_working.emit(self.__id, atu_conf, ks833_status)
            time.sleep(3)  # 等待数据采样完成

        # 清理现场 终止输出
        self.on_owari()

    def abort(self):
        self.sig_output.emit("下发终止命令 {}".format(self.__id))
        self.__abort = True


class Tab_9_Report(QWidget):

    sig_abort_workers = pyqtSignal()
    deviceModule = mengy_spac800

    def __init__(self, serRecvDir, sendQueuePack, app, logger=logger):
        super().__init__()
        self.serRecvDir = serRecvDir
        self.sendQueuePack = sendQueuePack
        self.app = app
        self.logger = logger
        self.serialObj = serial.Serial(port=None)
        self.rangeType = 0
        self.initUI()
        self.__workers_done = 0
        self.__threads = []
        self.refreshTime = time.time()
        self.logger.info("this is tab9 report logger")
        self.setObjectName("tab9")
        self.ks833Gen = KS833_PACK_GEN(logger)
        layout = QGridLayout(self)
        layout.addWidget(self.statisticGroupBox, 0, 0, 1, 1)  # 测试报告

    def initUI(self):
        """初始化显示构件"""
        self.statisticPrtTolerantValues = [0.5, 0.5, 0.5, 0.5]
        self.statisticMeaTolerantValues = [0.5, 0.5, 0.2, 2, 0.005, 0.01]

        self.meaGradeValues = [1, 1, 60]

        self.reportPrtData = {}
        self.reportMeaData = {}
        # 输入量指标
        self.createPrtSubGroupBox()
        self.createMeaSubGroupBox()
        # 报告输出
        self.creatrReportGenSubGroubBox()
        # 功率源
        self.createSerialWidget()
        self.createPowerLogGroupBox()
        # 创建表格
        self.createStatisticPrtTable()
        self.createStatisticMeaTable()
        self.createStatisticGroupBox()

    def getSerialPorts(self):
        """获取串口列表"""
        self.serialPortList = tuple(serial.tools.list_ports.comports())[::-1]
        if len(self.serialPortList) > 0:
            for serialPort in self.serialPortList:
                yield "{}".format(serialPort.device)
            self.serialObj.port = self.serialPortList[0].device

    def refreshSerialPort(self):
        """刷新串口列表"""
        self.serialPortNameComboBox.clear()
        self.serialPortNameComboBox.addItems(self.getSerialPorts())

    def setSerialPortName(self, serialPortName):
        """设置串口"""
        self.logger.info("serialPortName | {}".format(serialPortName))
        originPort = self.serialObj.port
        newPort = serialPortName.split("|")[0].strip()
        if originPort != newPort:
            self.logger.info("chage serial port | {} --> {}".format(originPort, newPort))
            self.serialObj.port = newPort

    def setSerialPortRate(self, baudrate):
        """串口波特率"""
        self.logger.info("baudrate | {}".format(baudrate))
        self.serialObj.baudrate = int(baudrate)
        self.serialObj.timeout = 3600 / self.serialObj.baudrate

    def setSerialDataBit(self, bytesize):
        """串口数据位长度"""
        self.logger.info("bytesize | {}".format(bytesize))
        self.serialObj.bytesize = int(bytesize)

    def setSerialStopBit(self, stopbits):
        """串口停止位"""
        self.logger.info("stopbits | {}".format(stopbits))
        self.serialObj.stopbits = float(stopbits)

    def setSerialParity(self, parity):
        """串口校验位"""
        self.logger.info("parity | {}".format(parity))
        self.serialObj.parity = parity

    def initStaticsValue(self):
        self.statisticPrtIs.setValue(5)
        self.statisticPrtUs.setValue(100)
        self.statisticPrtIxs.setValue(1)
        self.statisticPrtUxs.setValue(100)
        self.statisticMeaUs.setValue(220)
        self.statisticMeaIs.setValue(5)
        self.meaGradeCombRg.setValue(0)

    def _start_worker(self):
        """启动控制功率源线程"""
        self.__workers_done = 0
        self.__threads = []
        self.threads_num = 1
        worker = AutoTestWorker(
            id=0, app=self.app, serialObj=self.serialObj, rangeType=self.rangeType, deviceModule=self.deviceModule, ks833Gen=self.ks833Gen
        )
        thread = QThread()
        thread.setObjectName("thread_ks833")
        self.__threads.append((thread, worker))
        worker.moveToThread(thread)
        worker.sig_working.connect(self.on_worker_working)
        worker.sig_ks833_status.connect(self.on_ks833_status_change)
        worker.sig_finished.connect(self.on_worker_finished)
        worker.sig_output.connect(self.on_worker_output)
        self.sig_abort_workers.connect(worker.abort)
        thread.started.connect(worker.work)
        thread.start()

    def dealAutoTestMea(self, press):
        """自动测试按钮处理 测量板"""
        self.logger.info("self.dealAutoTestMeaButton clicked status {}".format(press))
        if press:
            self.rangeType = 0
            self.serialPortNameComboBox.setDisabled(True)
            self.dealAutoTestPrtButton.setDisabled(True)
            self.dealAutoTestMeaButton.setChecked(True)
            self.dealAutoTestMeaButton.setText("终止测试")
            self._start_worker()
            self.initStaticsValue()
        else:
            self.dealAutoTestMeaButton.setText("终止中..")
            self.dealAutoTestMeaButton.setDisabled(True)
            self.sig_abort_workers.emit()

    def dealAutoTestPrt(self, press):
        """自动测试按钮处理 保护"""
        self.logger.info("self.dealAutoTestPrtButton clicked status {}".format(press))
        if press:
            choice = QMessageBox.question(self, "警告", "测试台是否已合上空气开关？", QMessageBox.Yes | QMessageBox.No)
            if choice == QMessageBox.No:
                self.dealAutoTestPrtButton.setChecked(False)
                return
            self.rangeType = 1
            self.serialPortNameComboBox.setDisabled(True)
            self.dealAutoTestMeaButton.setDisabled(True)
            self.dealAutoTestPrtButton.setChecked(True)
            self.dealAutoTestPrtButton.setText("终止测试")
            self._start_worker()
            self.initStaticsValue()
        else:
            self.dealAutoTestPrtButton.setText("终止中..")
            self.dealAutoTestPrtButton.setDisabled(True)
            self.sig_abort_workers.emit()

    @pyqtSlot()
    def abort_workers(self):
        self.powerLogWidget.appendPlainText("Asking each worker to abort")
        self.sig_abort_workers.emit()
        self.serialObj.close()

    @pyqtSlot(int, mengy_spac800.AutoTestUpdateConf, KS833_OutputStatus)
    def on_worker_working(self, _id, atu_conf: mengy_spac800.AutoTestUpdateConf, ks833_status: KS833_OutputStatus):
        """slot for sig_working"""
        self.dealKS833Output(atu_conf, ks833_status)

    @pyqtSlot(int, KS833_OutputStatus)
    def on_ks833_status_change(self, _id, ks833_status: KS833_OutputStatus):
        """slot for sig_ks833_status"""
        self.dealKS833Output_Second(ks833_status)

    def dealKS833Output_Second(self, ks833_status):
        # self.powerLogWidget.appendPlainText("功率源输出状态 {}".format(ks833_status))
        # 电压标准值处理
        self.prtGradeCombU.setValue(ks833_status.Ua_Rms * (3 ** 0.5) / self.statisticPrtUs.value())
        self.prtGradeCombUx.setValue(ks833_status.Ua_Rms * (3 ** 0.5) / self.statisticPrtUxs.value())
        self.meaGradeCombU.setValue(ks833_status.Ua_Rms / self.statisticMeaUs.value())
        # 电流标准值
        self.prtGradeCombI.setValue(ks833_status.Ia_Rms / self.statisticPrtIs.value())
        self.prtGradeCombIx.setValue(ks833_status.Ia_Rms / self.statisticPrtIxs.value())
        self.meaGradeCombI.setValue(ks833_status.Ia_Rms / self.statisticMeaIs.value())
        # 角度
        self.meaGradeCombRg.setValue((360 - ks833_status.Ia_Ang) % 360)

    def _location_data_in_prt_tb(self, key_name):
        for idx, conf in enumerate(self.deviceModule.REPORT_PRT_CONFS):
            if key_name.startswith(conf.name.lower()):
                self.powerLogWidget.appendPlainText("更新保护测量数据 | {} --> {}".format(key_name, self.statisticPrtTW.item(idx, 0).text()))
                return idx, self.statisticPrtTW.item(idx, 0).text()

    def _location_data_in_mea_tb(self, key_name):
        for idx, conf in enumerate(mengy_spac800.REPORT_MEA_CONFS):
            if key_name.startswith(conf.name.lower()):
                self.powerLogWidget.appendPlainText("更新IDIN201F测量数据 | {} --> {}".format(key_name, self.statisticMeaTW.item(idx, 0).text()))
                return idx, self.statisticMeaTW.item(idx, 0).text()

    def _get_mea_data_bytes(self, conf):
        dataBytes = b"".join(self.serRecvDir.get(addr, b"\xff\xff") for addr in conf.dataAddress)
        self.logger.debug("name {} | data {} | dataBYtes {}".format(conf.name, bytes2Int(dataBytes, signed=conf.signed), bytesPuttyPrint(dataBytes)))
        return dataBytes

    def dealKS833Output(self, atu_conf, ks833_status):
        dataPrt = [bytes2Int(self.serRecvDir.get(conf.dataAddress)) / conf.rate for conf in self.deviceModule.REPORT_PRT_CONFS]
        dataMea = [bytes2Int(self._get_mea_data_bytes(conf), signed=conf.signed) / conf.rate for conf in mengy_spac800.REPORT_MEA_CONFS]
        self.powerLogWidget.appendPlainText("更新测量数据 | {} ".format(atu_conf))
        if atu_conf.pom == 1:  # 保护测量
            for key_name in atu_conf.keyList:
                data_idx, text = self._location_data_in_prt_tb(key_name)
                self.reportPrtData["prt_{}".format(key_name)] = (dataPrt[data_idx], text)

        else:
            for key_name in atu_conf.keyList:
                data_idx, text = self._location_data_in_mea_tb(key_name)
                self.reportMeaData["mea_{}".format(key_name)] = (dataMea[data_idx], text)
        # 更新数据
        self.reportBrowser.updateData()

    @pyqtSlot(int)
    def on_worker_finished(self, worker_id):
        # self.powerLogWidget.appendPlainText("任务 {} 已结束".format(worker_id))
        self.__workers_done += 1

        thread, _ = self.__threads[worker_id]
        thread.quit()
        thread.wait()
        self.serialObj.close()

        if self.__workers_done == self.threads_num:
            self.powerLogWidget.appendPlainText("全部任务已结束")
            self.dealAutoTestMeaButton.setChecked(False)
            self.dealAutoTestMeaButton.setDisabled(False)
            self.dealAutoTestMeaButton.setText("测量测试")
            self.dealAutoTestPrtButton.setChecked(False)
            self.dealAutoTestPrtButton.setDisabled(False)
            self.dealAutoTestPrtButton.setText("保护测试")
            self.serialPortNameComboBox.setDisabled(False)

    @pyqtSlot(str)
    def on_worker_output(self, msg):
        self.powerLogWidget.appendPlainText(msg)

    def createSerialWidget(self):
        self.serialWidget = QGroupBox("功率源串口设置")
        serialLayout = QGridLayout(self.serialWidget)
        serialLayout.setContentsMargins(0, 0, 0, 0)
        serialLayout.setSpacing(0)

        self.serialPortNameComboBox = QComboBox()
        self.serialPortNameComboBox.addItems(self.getSerialPorts())
        self.serialPortNameComboBox.activated[str].connect(self.setSerialPortName)
        sserialNameLabel = QLabel("串口:")

        self.serialPortFreshButton = QPushButton("刷新串口")
        self.serialPortFreshButton.clicked[bool].connect(self.refreshSerialPort)

        self.dealAutoTestMeaButton = QPushButton("测量测试")
        self.dealAutoTestMeaButton.setCheckable(True)
        self.dealAutoTestMeaButton.setChecked(False)
        self.dealAutoTestMeaButton.clicked[bool].connect(self.dealAutoTestMea)

        self.dealAutoTestPrtButton = QPushButton("保护测试")
        self.dealAutoTestPrtButton.setCheckable(True)
        self.dealAutoTestPrtButton.setChecked(False)
        self.dealAutoTestPrtButton.clicked[bool].connect(self.dealAutoTestPrt)

        tempLayout = QHBoxLayout()
        tempLayout.addWidget(sserialNameLabel)
        tempLayout.addWidget(self.serialPortNameComboBox)
        serialLayout.addLayout(tempLayout, 0, 0)
        serialLayout.addWidget(self.serialPortFreshButton, 0, 1)
        serialLayout.addWidget(self.dealAutoTestMeaButton, 0, 2)
        serialLayout.addWidget(self.dealAutoTestPrtButton, 0, 3)

        self.serialPortRateComboBox = QComboBox()
        self.serialPortRateComboBox.addItems((str(i) for i in BAUD_LIST))
        self.serialPortRateComboBox.setCurrentIndex(BAUD_LIST.index(19200))
        self.setSerialPortRate("19200")
        self.serialPortRateComboBox.activated[str].connect(self.setSerialPortRate)

        self.serialPortDataBitComboBox = QComboBox()
        self.serialPortDataBitComboBox.addItems(["8", "7", "6", "5"])
        self.serialPortDataBitComboBox.activated[str].connect(self.setSerialDataBit)

        self.serialPortStopBitComboBox = QComboBox()
        self.serialPortStopBitComboBox.addItems(["1", "1.5", "2"])
        self.serialPortStopBitComboBox.activated[str].connect(self.setSerialStopBit)

        self.serialPortParityComboBox = QComboBox()
        self.serialPortParityComboBox.setToolTip("N: 无校验\nE: 奇校验\nO: 偶校验\nM: 校验位始终为1\nS: 校验位始终为0")
        self.serialPortParityComboBox.addItems(["N", "E", "O", "M", "S"])
        self.serialPortParityComboBox.activated[str].connect(self.setSerialParity)

        tempLayout = QHBoxLayout()
        tempLayout.addWidget(QLabel("波特率:"))
        tempLayout.addWidget(self.serialPortRateComboBox)
        tempLayout.addWidget(QLabel("数据位:"))
        tempLayout.addWidget(self.serialPortDataBitComboBox)

        tempLayout.addWidget(QLabel("停止位:"))
        tempLayout.addWidget(self.serialPortStopBitComboBox)
        tempLayout.addWidget(QLabel("校验位:"))
        tempLayout.addWidget(self.serialPortParityComboBox)
        tempLayout.addStretch(1)
        serialLayout.addLayout(tempLayout, 1, 0, 1, 4)

    def createPowerLogGroupBox(self):
        self.powerLogGroupBox = QGroupBox("测试信息")
        layout = QGridLayout(self.powerLogGroupBox)
        self.powerLogWidget = QPlainTextEdit()
        self.powerLogWidget.setCenterOnScroll(True)
        self.powerLogWidget.ensureCursorVisible()
        layout.addWidget(self.powerLogWidget, 0, 0)

    def createPrtSubGroupBox(self):
        self.prtBaseGroupBox = QGroupBox("保护额定值")
        subLayout = QGridLayout(self.prtBaseGroupBox)
        subLayout.addWidget(QLabel("电流"), 0, 0)
        self.statisticPrtIs = QDoubleSpinBox()
        self.statisticPrtIs.setRange(0.0, 5000.0)
        self.statisticPrtIs.setDecimals(1)
        self.statisticPrtIs.setSingleStep(0.5)
        self.statisticPrtIs.setSuffix("A")
        addTooltipInSpinBox(self.statisticPrtIs, (0.0, 5000.0))
        self.statisticPrtIs.setValue(5)
        self.statisticPrtIs.valueChanged.connect(self.updateStatisticPrtTable)
        subLayout.addWidget(self.statisticPrtIs, 0, 1)

        subLayout.addWidget(QLabel("电压"), 1, 0)
        self.statisticPrtUs = QDoubleSpinBox()
        self.statisticPrtUs.setRange(0.0, 40000.0)
        self.statisticPrtUs.setDecimals(1)
        self.statisticPrtUs.setSingleStep(20)
        self.statisticPrtUs.setSuffix("V")
        addTooltipInSpinBox(self.statisticPrtUs, (0.0, 40000.0))
        self.statisticPrtUs.setValue(100)
        self.statisticPrtUs.valueChanged.connect(self.updateStatisticPrtTable)
        subLayout.addWidget(self.statisticPrtUs, 1, 1)

        subLayout.addWidget(QLabel("Ix"), 2, 0)
        self.statisticPrtIxs = QDoubleSpinBox()
        self.statisticPrtIxs.setDecimals(1)
        self.statisticPrtIxs.setSingleStep(0.5)
        self.statisticPrtIxs.setSuffix("A")
        addTooltipInSpinBox(self.statisticPrtIxs, (0.0, 5000.0))
        self.statisticPrtIxs.setValue(1)
        self.statisticPrtIxs.valueChanged.connect(self.updateStatisticPrtTable)
        subLayout.addWidget(self.statisticPrtIxs, 2, 1)

        subLayout.addWidget(QLabel("Ux"), 3, 0)
        self.statisticPrtUxs = QDoubleSpinBox()
        self.statisticPrtUxs.setDecimals(1)
        self.statisticPrtUxs.setSingleStep(20)
        self.statisticPrtUxs.setSuffix("V")
        addTooltipInSpinBox(self.statisticPrtUxs, (0.0, 40000.0))
        self.statisticPrtUxs.setValue(100)
        self.statisticPrtUxs.valueChanged.connect(self.updateStatisticPrtTable)
        subLayout.addWidget(self.statisticPrtUxs, 3, 1)

        self.prtGradeGroupBox = QGroupBox("保护档位")
        subLayout = QGridLayout(self.prtGradeGroupBox)
        subLayout.addWidget(QLabel("电流档位"), 0, 0)
        self.prtGradeCombI = QDoubleSpinBox()
        self.prtGradeCombI.setRange(0.05, 1000)
        addTooltipInSpinBox(self.prtGradeCombI, (0.00, 1000))
        self.prtGradeCombI.setDecimals(2)
        self.prtGradeCombI.setSingleStep(0.1)
        self.prtGradeCombI.setValue(1)
        self.prtGradeCombI.valueChanged.connect(self.updateStatisticPrtTable)
        subLayout.addWidget(self.prtGradeCombI, 0, 1)
        subLayout.addWidget(QLabel("电压档位"), 1, 0)
        self.prtGradeCombU = QDoubleSpinBox()
        self.prtGradeCombU.setRange(0.05, 1000)
        addTooltipInSpinBox(self.prtGradeCombU, (0.00, 1000))
        self.prtGradeCombU.setDecimals(2)
        self.prtGradeCombU.setSingleStep(0.1)
        self.prtGradeCombU.setValue(1)
        self.prtGradeCombU.valueChanged.connect(self.updateStatisticPrtTable)
        subLayout.addWidget(self.prtGradeCombU, 1, 1)
        subLayout.addWidget(QLabel("Ix档位"), 2, 0)
        self.prtGradeCombIx = QDoubleSpinBox()
        addTooltipInSpinBox(self.prtGradeCombIx, (0.00, 1000))
        self.prtGradeCombIx.setDecimals(2)
        self.prtGradeCombIx.setSingleStep(0.1)
        self.prtGradeCombIx.setValue(1)
        self.prtGradeCombIx.valueChanged.connect(self.updateStatisticPrtTable)
        subLayout.addWidget(self.prtGradeCombIx, 2, 1)
        subLayout.addWidget(QLabel("Ux档位"), 3, 0)
        self.prtGradeCombUx = QDoubleSpinBox()
        addTooltipInSpinBox(self.prtGradeCombUx, (0.00, 1000))
        self.prtGradeCombUx.setDecimals(2)
        self.prtGradeCombUx.setSingleStep(0.1)
        self.prtGradeCombUx.setValue(1)
        self.prtGradeCombUx.valueChanged.connect(self.updateStatisticPrtTable)
        subLayout.addWidget(self.prtGradeCombUx, 3, 1)

        self.prtTolerantGroupBox = QGroupBox("误差许可")
        subLayout = QGridLayout(self.prtTolerantGroupBox)

        subLayout.addWidget(QLabel("电流"), 0, 0)
        self.statisticPrtIt = QDoubleSpinBox()
        self.statisticPrtIt.setDecimals(1)
        self.statisticPrtIt.setSingleStep(0.1)
        self.statisticPrtIt.setSuffix("%")
        addTooltipInSpinBox(self.statisticPrtIt, (0.0, 5.0))
        self.statisticPrtIt.setValue(self.statisticPrtTolerantValues[0])
        self.statisticPrtIt.valueChanged.connect(self.dealStatisticPrtValueChanged)
        subLayout.addWidget(self.statisticPrtIt, 0, 1)

        subLayout.addWidget(QLabel("电压"), 1, 0)
        self.statisticPrtUt = QDoubleSpinBox()
        self.statisticPrtUt.setDecimals(1)
        self.statisticPrtUt.setSingleStep(0.1)
        self.statisticPrtUt.setSuffix("%")
        addTooltipInSpinBox(self.statisticPrtUt, (0.0, 5.0))
        self.statisticPrtUt.setValue(self.statisticPrtTolerantValues[1])
        self.statisticPrtUt.valueChanged.connect(self.dealStatisticPrtValueChanged)
        subLayout.addWidget(self.statisticPrtUt, 1, 1)

        subLayout.addWidget(QLabel("Ix"), 2, 0)
        self.statisticPrtIxt = QDoubleSpinBox()
        self.statisticPrtIxt.setDecimals(1)
        self.statisticPrtIxt.setSingleStep(0.1)
        self.statisticPrtIxt.setSuffix("%")
        addTooltipInSpinBox(self.statisticPrtIxt, (0.0, 5.0))
        self.statisticPrtIxt.setValue(self.statisticPrtTolerantValues[2])
        self.statisticPrtIxt.valueChanged.connect(self.dealStatisticPrtValueChanged)
        subLayout.addWidget(self.statisticPrtIxt, 2, 1)

        subLayout.addWidget(QLabel("Ux"), 3, 0)
        self.statisticPrtUxt = QDoubleSpinBox()
        self.statisticPrtUxt.setDecimals(1)
        self.statisticPrtUxt.setSingleStep(0.1)
        self.statisticPrtUxt.setSuffix("%")
        addTooltipInSpinBox(self.statisticPrtUxt, (0.0, 5.0))
        self.statisticPrtUxt.setValue(self.statisticPrtTolerantValues[3])
        self.statisticPrtUxt.valueChanged.connect(self.dealStatisticPrtValueChanged)
        subLayout.addWidget(self.statisticPrtUxt, 3, 1)

    def createMeaSubGroupBox(self):
        self.meaBaseGroupBox = QGroupBox("测量额定值")
        subLayout = QGridLayout(self.meaBaseGroupBox)
        subLayout.addWidget(QLabel("电流"), 0, 0)
        self.statisticMeaIs = QDoubleSpinBox()
        addTooltipInSpinBox(self.statisticMeaIs, (0.0, 5000.0))
        self.statisticMeaIs.setDecimals(1)
        self.statisticMeaIs.setSingleStep(0.5)
        self.statisticMeaIs.setSuffix("A")
        self.statisticMeaIs.setValue(5)
        self.statisticMeaIs.valueChanged.connect(self.updateStatisticMeaTable)
        subLayout.addWidget(self.statisticMeaIs, 0, 1)

        subLayout.addWidget(QLabel("电压"), 1, 0)
        self.statisticMeaUs = QDoubleSpinBox()
        addTooltipInSpinBox(self.statisticMeaUs, (0.0, 40000.0))
        self.statisticMeaUs.setDecimals(1)
        self.statisticMeaUs.setSingleStep(20)
        self.statisticMeaUs.setSuffix("V")
        self.statisticMeaUs.setValue(220)
        self.statisticMeaUs.valueChanged.connect(self.updateStatisticMeaTable)
        subLayout.addWidget(self.statisticMeaUs, 1, 1)

        self.meaTolerantGroupBox = QGroupBox("测量误差许可")
        subLayout = QGridLayout(self.meaTolerantGroupBox)
        subLayout.addWidget(QLabel("电流"), 0, 0)
        self.statisticMeaIt = QDoubleSpinBox()
        addTooltipInSpinBox(self.statisticMeaIt, (0.0, 5.0))
        self.statisticMeaIt.setDecimals(1)
        self.statisticMeaIt.setSingleStep(0.1)
        self.statisticMeaIt.setSuffix("%")
        self.statisticMeaIt.setValue(self.statisticMeaTolerantValues[0])
        self.statisticMeaIt.valueChanged.connect(self.dealStatisticMeaValueChanged)
        subLayout.addWidget(self.statisticMeaIt, 0, 1)

        subLayout.addWidget(QLabel("电压"), 1, 0)
        self.statisticMeaUt = QDoubleSpinBox()
        addTooltipInSpinBox(self.statisticMeaUt, (0.0, 5.0))
        self.statisticMeaUt.setDecimals(1)
        self.statisticMeaUt.setSingleStep(0.1)
        self.statisticMeaUt.setSuffix("%")
        self.statisticMeaUt.setValue(self.statisticMeaTolerantValues[1])
        self.statisticMeaUt.valueChanged.connect(self.dealStatisticMeaValueChanged)
        subLayout.addWidget(self.statisticMeaUt, 1, 1)

        subLayout.addWidget(QLabel("有功"), 2, 0)
        self.statisticMeaPPt = QDoubleSpinBox()
        addTooltipInSpinBox(self.statisticMeaPPt, (0.0, 5.0))
        self.statisticMeaPPt.setDecimals(1)
        self.statisticMeaPPt.setSingleStep(0.1)
        self.statisticMeaPPt.setSuffix("%")
        self.statisticMeaPPt.setValue(self.statisticMeaTolerantValues[2])
        self.statisticMeaPPt.valueChanged.connect(self.dealStatisticMeaValueChanged)
        subLayout.addWidget(self.statisticMeaPPt, 2, 1)

        subLayout.addWidget(QLabel("无功"), 3, 0)
        self.statisticMeaNPt = QDoubleSpinBox()
        addTooltipInSpinBox(self.statisticMeaNPt, (0.0, 5.0))
        self.statisticMeaNPt.setDecimals(1)
        self.statisticMeaNPt.setSingleStep(0.1)
        self.statisticMeaNPt.setSuffix("%")
        self.statisticMeaNPt.setValue(self.statisticMeaTolerantValues[3])
        self.statisticMeaNPt.valueChanged.connect(self.dealStatisticMeaValueChanged)
        subLayout.addWidget(self.statisticMeaNPt, 3, 1)

        subLayout.addWidget(QLabel("功因"), 4, 0)
        self.statisticMeaCost = QDoubleSpinBox()
        addTooltipInSpinBox(self.statisticMeaCost, (0.0, 0.05))
        self.statisticMeaCost.setDecimals(3)
        self.statisticMeaCost.setSingleStep(0.001)
        self.statisticMeaCost.setValue(self.statisticMeaTolerantValues[4])
        self.statisticMeaCost.valueChanged.connect(self.dealStatisticMeaValueChanged)
        subLayout.addWidget(self.statisticMeaCost, 4, 1)

        subLayout.addWidget(QLabel("频率"), 5, 0)
        self.statisticMeaFqt = QDoubleSpinBox()
        addTooltipInSpinBox(self.statisticMeaFqt, (0.0, 5.0))
        self.statisticMeaFqt.setDecimals(2)
        self.statisticMeaFqt.setSingleStep(0.01)
        self.statisticMeaFqt.setSuffix("Hz")
        self.statisticMeaFqt.setValue(self.statisticMeaTolerantValues[5])
        self.statisticMeaFqt.valueChanged.connect(self.dealStatisticMeaValueChanged)
        subLayout.addWidget(self.statisticMeaFqt, 5, 1)

        self.meaGradeFroupBox = QGroupBox("测量档位")
        subLayout = QGridLayout(self.meaGradeFroupBox)
        subLayout.addWidget(QLabel("电流档位"), 0, 0)
        self.meaGradeCombI = QDoubleSpinBox()
        self.meaGradeCombI.setRange(0.05, 1000)
        addTooltipInSpinBox(self.meaGradeCombI, (0.05, 1000))
        self.meaGradeCombI.setDecimals(2)
        self.meaGradeCombI.setSingleStep(0.1)
        self.meaGradeCombI.setValue(1)

        self.meaGradeCombI.valueChanged.connect(self.dealStatisticMeaValueChanged)
        subLayout.addWidget(self.meaGradeCombI, 0, 1)
        subLayout.addWidget(QLabel("电压档位"), 1, 0)
        self.meaGradeCombU = QDoubleSpinBox()
        self.meaGradeCombU.setRange(0.05, 1000)
        addTooltipInSpinBox(self.meaGradeCombU, (0.05, 1000))
        self.meaGradeCombU.setDecimals(2)
        self.meaGradeCombU.setSingleStep(0.1)
        self.meaGradeCombU.setValue(1)

        self.meaGradeCombU.valueChanged.connect(self.dealStatisticMeaValueChanged)
        subLayout.addWidget(self.meaGradeCombU, 1, 1)
        subLayout.addWidget(QLabel("U/I角度"), 2, 0)
        self.meaGradeCombRg = QDoubleSpinBox()
        addTooltipInSpinBox(self.meaGradeCombRg, (-361, 361))
        self.meaGradeCombRg.setDecimals(1)
        self.meaGradeCombRg.setSingleStep(30)
        self.meaGradeCombRg.setValue(60)
        self.meaGradeCombRg.setSuffix("°")
        self.meaGradeCombRg.valueChanged.connect(self.dealStatisticMeaValueChanged)
        subLayout.addWidget(self.meaGradeCombRg, 2, 1)

    def creatrReportGenSubGroubBox(self):
        self.reportGenGroupBox = QGroupBox("测试数据处理")
        subLayout = QGridLayout(self.reportGenGroupBox)
        # self.reportClearBtn = QPushButton("清零")
        # self.reportClearBtn.clicked.connect(self.reportClearData)
        self.reportBrowseBtn = QPushButton("查看与输出")
        self.reportBrowser = ReportBrowser(parent=self, serRecvDir=self.serRecvDir, sendQueuePack=self.sendQueuePack, logger=self.logger)
        self.reportBrowseBtn.clicked.connect(self.browseReport)
        subLayout.addWidget(self.reportBrowseBtn, 0, 0)
        # subLayout.addWidget(self.reportClearBtn, 1, 0)

    def createStatisticGroupBox(self):
        self.statisticGroupBox = QWidget()
        layout = QGridLayout(self.statisticGroupBox)
        layout.addWidget(self.prtBaseGroupBox, 0, 0, 5, 1)
        layout.addWidget(self.prtGradeGroupBox, 5, 0, 6, 1)
        layout.addWidget(self.prtTolerantGroupBox, 11, 0, 4, 1)
        layout.addWidget(self.reportGenGroupBox, 15, 0, 1, 1)
        layout.addWidget(self.statisticPrtTW, 0, 2, 9, 4)
        layout.addWidget(self.serialWidget, 9, 2, 3, 4)
        layout.addWidget(self.powerLogGroupBox, 12, 2, 4, 4)

        layout.addWidget(self.statisticMeaTW, 0, 6, 17, 6)
        layout.addWidget(self.meaBaseGroupBox, 0, 12, 3, 1)
        layout.addWidget(self.meaGradeFroupBox, 3, 12, 4, 1)
        layout.addWidget(self.meaTolerantGroupBox, 7, 12, 7, 1)

    def createStatisticPrtTable(self):
        self.statisticPrtTW = QTableWidget(len(self.deviceModule.REPORT_PRT_CONFS), 3)
        self.statisticPrtTW.setVerticalHeaderLabels((i.name for i in self.deviceModule.REPORT_PRT_CONFS))
        self.statisticPrtTW.setHorizontalHeaderLabels(("测量值", "标准值", "误差"))
        self.statisticPrtTW.resizeRowsToContents()
        header = self.statisticPrtTW.horizontalHeader()
        header.setSectionResizeMode(QHeaderView.Stretch)
        header = self.statisticPrtTW.verticalHeader()
        header.setSectionResizeMode(QHeaderView.Stretch)

        self.statisticPrtTW.setEditTriggers(QTableWidget.NoEditTriggers)
        for r in range(self.statisticPrtTW.rowCount()):
            for c in range(self.statisticPrtTW.columnCount()):
                self.statisticPrtTW.setItem(r, c, QTableWidgetItem(""))
                self.statisticPrtTW.item(r, c).setTextAlignment(Qt.AlignCenter)

    def createStatisticMeaTable(self):
        self.statisticMeaTW = QTableWidget(len(mengy_spac800.REPORT_MEA_CONFS), 3)
        self.statisticMeaTW.setVerticalHeaderLabels((i.name for i in mengy_spac800.REPORT_MEA_CONFS))
        self.statisticMeaTW.setHorizontalHeaderLabels(("测量值", "标准值", "误差"))
        header = self.statisticMeaTW.horizontalHeader()
        header.setSectionResizeMode(QHeaderView.Stretch)
        header = self.statisticMeaTW.verticalHeader()
        header.setSectionResizeMode(QHeaderView.Stretch)

        self.statisticMeaTW.setEditTriggers(QTableWidget.NoEditTriggers)
        for r in range(self.statisticMeaTW.rowCount()):
            for c in range(self.statisticMeaTW.columnCount()):
                self.statisticMeaTW.setItem(r, c, QTableWidgetItem(""))
                self.statisticMeaTW.item(r, c).setTextAlignment(Qt.AlignCenter)

    def dealStatisticPrtValueChanged(self):
        """处理测试报告界面中 额定值 误差许可 档位发生的变化"""
        sender = self.sender()
        if sender in (self.statisticPrtIt, self.statisticPrtUt, self.statisticPrtIxt, self.statisticPrtUxt):
            idx = (self.statisticPrtIt, self.statisticPrtUt, self.statisticPrtIxt, self.statisticPrtUxt).index(sender)
            self.statisticPrtTolerantValues[idx] = sender.value()
        self.updateStatisticPrtTable()

    def dealStatisticMeaValueChanged(self):
        """处理测试报告界面中 额定值 误差许可 档位发生的变化"""
        sender = self.sender()
        if sender in (
            self.statisticMeaIt,
            self.statisticMeaUt,
            self.statisticMeaPPt,
            self.statisticMeaNPt,
            self.statisticMeaCost,
            self.statisticMeaFqt,
        ):
            idx = (
                self.statisticMeaIt,
                self.statisticMeaUt,
                self.statisticMeaPPt,
                self.statisticMeaNPt,
                self.statisticMeaCost,
                self.statisticMeaFqt,
            ).index(sender)
            self.statisticMeaTolerantValues[idx] = sender.value()
        self.updateStatisticMeaTable()

    def updateStatisticPrtTable(self):
        """更新测试表格数据"""
        # self.logger.debug("invoke spac800 updateStatisticPrtTable")
        if any((key not in self.serRecvDir.keys() for key in range(0x000E, 0x0020 + 1))):
            self.logger.debug("no enought data in spac800 updateStatisticPrtTable")
            return
        # 保护监视部分
        for idx, conf in enumerate(mengy_spac800.REPORT_PRT_CONFS):
            data = bytes2Int(self.serRecvDir.get(conf.dataAddress)) / conf.rate
            self.statisticPrtTW.item(idx, 0).setText("{:{}} {}".format(data, conf.format, conf.unit))
            if idx in (0, 1, 2):
                sdata = self.statisticPrtUs.value() * self.prtGradeCombU.value()
                toleranceBase = self.statisticPrtTolerantValues[1]
            elif idx in (3, 4, 5):
                sdata = self.statisticPrtIs.value() * self.prtGradeCombI.value()
                toleranceBase = self.statisticPrtTolerantValues[0]
            elif idx == 7:
                sdata = self.statisticPrtIxs.value() * self.prtGradeCombIx.value()
                toleranceBase = self.statisticPrtTolerantValues[2]
            elif idx == 6:
                sdata = self.statisticPrtUxs.value() * self.prtGradeCombUx.value()
                toleranceBase = self.statisticPrtTolerantValues[3]
            self.statisticPrtTW.item(idx, 1).setText("{:{}} {}".format(sdata, conf.format, conf.unit))
            if sdata == 0:
                sdata = 10 ** -5
            tolerance = (data - sdata) / sdata
            if tolerance <= 100:
                self.statisticPrtTW.item(idx, 2).setText("{:+.2%}".format(tolerance))
            else:
                self.statisticPrtTW.item(idx, 2).setText(">10000%")
            if abs(tolerance) * 100 <= toleranceBase:
                self.statisticPrtTW.item(idx, 2).setBackground(QColor("#00FF00"))
            else:
                self.statisticPrtTW.item(idx, 2).setBackground(QColor("#FF0000"))

    def updateStatisticMeaTable(self):
        """更新测试表格数据"""
        if any((key not in self.serRecvDir.keys() for key in range(0x0023, 0x003F + 1))):
            return
        # 测量板部分
        meaI = self.statisticMeaIs.value() * self.meaGradeCombI.value()
        meaU = self.statisticMeaUs.value() * self.meaGradeCombU.value()
        # testData = (5.01, 5.0, 4.99, 221, 220, 219, 551, 550, 549, 953, 952, 951, 0.501, 0.500, 0.499, 0.500, 49.98)
        for idx, conf in enumerate(mengy_spac800.REPORT_MEA_CONFS):
            dataByte = b"".join((self.serRecvDir.get(addr) for addr in conf.dataAddress))
            data = bytes2Int(dataByte, signed=conf.signed) / conf.rate
            # data = testData[idx]
            self.statisticMeaTW.item(idx, 0).setText("{:{}} {}".format(data, conf.format, conf.unit))
            self.logger.debug("name {} | data bytes {} | data {}".format(conf.name, bytesPuttyPrint(dataByte), data))
            if idx in (0, 1, 2):
                sdata = meaI
                toleranceBase = self.statisticMeaTolerantValues[0]
            elif idx in (3, 4, 5):
                sdata = meaU
                toleranceBase = self.statisticMeaTolerantValues[1]
            elif idx in (6, 7, 8):
                sdata = meaI * meaU * math.cos(math.radians(self.meaGradeCombRg.value()))
                toleranceBase = self.statisticMeaTolerantValues[2]
            elif idx in (9, 10, 11):
                sdata = meaI * meaU * math.sin(math.radians(self.meaGradeCombRg.value()))
                toleranceBase = self.statisticMeaTolerantValues[3]
            elif idx in (12, 13, 14, 15):
                sdata = math.cos(math.radians(self.meaGradeCombRg.value()))
                toleranceBase = self.statisticMeaTolerantValues[4]
            elif idx == 16:
                toleranceBase = self.statisticMeaTolerantValues[5]
                sdata = 50
            self.statisticMeaTW.item(idx, 1).setText("{:{}} {}".format(sdata, conf.format, conf.unit))
            if conf.toleranceType == "percent":
                if sdata == 0:
                    sdata = 10 ** -5
                tolerance = (data - sdata) / sdata
                if tolerance <= 100:
                    self.statisticMeaTW.item(idx, 2).setText("{:+.2%}".format(tolerance))
                else:
                    self.statisticMeaTW.item(idx, 2).setText(">10000%")
                if abs(tolerance) * 100 <= toleranceBase:
                    self.statisticMeaTW.item(idx, 2).setBackground(QColor("#00FF00"))
                else:
                    self.statisticMeaTW.item(idx, 2).setBackground(QColor("#FF0000"))
            elif conf.toleranceType == "absolute":
                tolerance = round((data - sdata), 4)
                self.statisticMeaTW.item(idx, 2).setText("{:{}} {}".format(tolerance, conf.format, conf.unit))
                if abs(tolerance) <= toleranceBase:
                    self.statisticMeaTW.item(idx, 2).setBackground(QColor("#00FF00"))
                else:
                    self.statisticMeaTW.item(idx, 2).setBackground(QColor("#FF0000"))

    def reportPrtClearData(self, e):
        self.reportPrtData.clear()
        self.logger.info("self.reportPrtData {}".format(self.reportPrtData))

    def reportMeaClearData(self, e):
        self.reportMeaData.clear()
        self.logger.info("self.reportMeaData {}".format(self.reportMeaData))

    def reportClearData(self):
        self.reportPrtData.clear()
        self.reportMeaData.clear()

    def browseReport(self):
        self.reportBrowser.updateData()
        self.reportBrowser.show()

    def rewriteMeaEnableWgt(self):
        """测量板使能影响的构件"""
        meaEnable = self.serRecvDir.get(0x0704)
        for w in (self.meaBaseGroupBox, self.meaTolerantGroupBox, self.meaGradeFroupBox, self.statisticMeaTW):
            if meaEnable == b"\x00\x01":  # 测量板投入
                w.setDisabled(False)
            else:
                w.setDisabled(True)

    def updateBySerialRecvBuff(self, changeAddrList):
        """数据缓存发生变化时需要刷新部分"""
        if any((i in changeAddrList for i in range(0x000A, 0x004F + 1))):
            self.updateStatisticPrtTable()
            self.updateStatisticMeaTable()
        if 0x0704 in changeAddrList:
            self.rewriteMeaEnableWgt()
        self.refreshTime = time.time()

    def updateByTimer(self, timeout=1):
        if time.time() - self.refreshTime > timeout:
            self.updateStatisticPrtTable()
            self.updateStatisticMeaTable()
            self.refreshTime = time.time()

    def updateUnconditional(self):
        """无条件刷新界面显示部分"""
        pass

    def updateWhole(self):
        """更新全体显示构件"""
        self.updateStatisticPrtTable()
        self.updateStatisticMeaTable()
        self.rewriteMeaEnableWgt()
        self.refreshTime = time.time()


class Tab_9_Report_BT(Tab_9_Report):
    deviceModule = mengy_spac800_bt

    def createPrtSubGroupBox(self):
        self.prtBaseGroupBox = QGroupBox("保护额定值")
        subLayout = QGridLayout(self.prtBaseGroupBox)

        subLayout.addWidget(QLabel("电压"), 0, 0)
        self.statisticPrtUs = QDoubleSpinBox()
        self.statisticPrtUs.setRange(0.0, 40000.0)
        self.statisticPrtUs.setDecimals(1)
        self.statisticPrtUs.setSingleStep(20)
        self.statisticPrtUs.setSuffix("V")
        addTooltipInSpinBox(self.statisticPrtUs, (0.0, 40000.0))
        self.statisticPrtUs.setValue(100)
        self.statisticPrtUs.valueChanged.connect(self.updateStatisticPrtTable)
        subLayout.addWidget(self.statisticPrtUs, 0, 1)

        subLayout.addWidget(QLabel("Ix"), 1, 0)
        self.statisticPrtIxs = QDoubleSpinBox()
        self.statisticPrtIxs.setDecimals(1)
        self.statisticPrtIxs.setSingleStep(0.5)
        self.statisticPrtIxs.setSuffix("A")
        addTooltipInSpinBox(self.statisticPrtIxs, (0.0, 5000.0))
        self.statisticPrtIxs.setValue(1)
        self.statisticPrtIxs.valueChanged.connect(self.updateStatisticPrtTable)
        subLayout.addWidget(self.statisticPrtIxs, 1, 1)

        subLayout.addWidget(QLabel("Ux"), 2, 0)
        self.statisticPrtUxs = QDoubleSpinBox()
        self.statisticPrtUxs.setDecimals(1)
        self.statisticPrtUxs.setSingleStep(20)
        self.statisticPrtUxs.setSuffix("V")
        addTooltipInSpinBox(self.statisticPrtUxs, (0.0, 40000.0))
        self.statisticPrtUxs.setValue(100)
        self.statisticPrtUxs.valueChanged.connect(self.updateStatisticPrtTable)
        subLayout.addWidget(self.statisticPrtUxs, 2, 1)

        self.prtGradeGroupBox = QGroupBox("保护档位")
        subLayout = QGridLayout(self.prtGradeGroupBox)
        subLayout.addWidget(QLabel("电压档位"), 0, 0)
        self.prtGradeCombU = QDoubleSpinBox()
        self.prtGradeCombU.setRange(0.05, 1000)
        addTooltipInSpinBox(self.prtGradeCombU, (0.05, 1000))
        self.prtGradeCombU.setDecimals(2)
        self.prtGradeCombU.setSingleStep(0.1)
        self.prtGradeCombU.setValue(1)
        self.prtGradeCombU.valueChanged.connect(self.updateStatisticPrtTable)
        subLayout.addWidget(self.prtGradeCombU, 0, 1)
        subLayout.addWidget(QLabel("Ix档位"), 1, 0)
        self.prtGradeCombIx = QDoubleSpinBox()
        addTooltipInSpinBox(self.prtGradeCombIx, (0.05, 1000))
        self.prtGradeCombIx.setDecimals(2)
        self.prtGradeCombIx.setSingleStep(0.1)
        self.prtGradeCombIx.setValue(1)
        self.prtGradeCombIx.valueChanged.connect(self.updateStatisticPrtTable)
        subLayout.addWidget(self.prtGradeCombIx, 1, 1)
        subLayout.addWidget(QLabel("Ux档位"), 2, 0)
        self.prtGradeCombUx = QDoubleSpinBox()
        addTooltipInSpinBox(self.prtGradeCombUx, (0.05, 1000))
        self.prtGradeCombUx.setDecimals(2)
        self.prtGradeCombUx.setSingleStep(0.1)
        self.prtGradeCombUx.setValue(1)
        self.prtGradeCombUx.valueChanged.connect(self.updateStatisticPrtTable)
        subLayout.addWidget(self.prtGradeCombUx, 2, 1)

        self.prtTolerantGroupBox = QGroupBox("误差许可")
        subLayout = QGridLayout(self.prtTolerantGroupBox)

        subLayout.addWidget(QLabel("电压"), 0, 0)
        self.statisticPrtUt = QDoubleSpinBox()
        self.statisticPrtUt.setDecimals(1)
        self.statisticPrtUt.setSingleStep(0.1)
        self.statisticPrtUt.setSuffix("%")
        addTooltipInSpinBox(self.statisticPrtUt, (0.0, 500.0))
        self.statisticPrtUt.setValue(self.statisticPrtTolerantValues[0])
        self.statisticPrtUt.valueChanged.connect(self.dealStatisticPrtValueChanged)
        subLayout.addWidget(self.statisticPrtUt, 0, 1)

        subLayout.addWidget(QLabel("Ix"), 1, 0)
        self.statisticPrtIxt = QDoubleSpinBox()
        self.statisticPrtIxt.setDecimals(1)
        self.statisticPrtIxt.setSingleStep(0.1)
        self.statisticPrtIxt.setSuffix("%")
        addTooltipInSpinBox(self.statisticPrtIxt, (0.0, 500.0))
        self.statisticPrtIxt.setValue(self.statisticPrtTolerantValues[1])
        self.statisticPrtIxt.valueChanged.connect(self.dealStatisticPrtValueChanged)
        subLayout.addWidget(self.statisticPrtIxt, 1, 1)

        subLayout.addWidget(QLabel("Ux"), 2, 0)
        self.statisticPrtUxt = QDoubleSpinBox()
        self.statisticPrtUxt.setDecimals(1)
        self.statisticPrtUxt.setSingleStep(0.1)
        self.statisticPrtUxt.setSuffix("%")
        addTooltipInSpinBox(self.statisticPrtUxt, (0.0, 500.0))
        self.statisticPrtUxt.setValue(self.statisticPrtTolerantValues[2])
        self.statisticPrtUxt.valueChanged.connect(self.dealStatisticPrtValueChanged)
        subLayout.addWidget(self.statisticPrtUxt, 2, 1)

    def dealStatisticPrtValueChanged(self):
        """处理测试报告界面中 额定值 误差许可 档位发生的变化"""
        sender = self.sender()
        if sender in (self.statisticPrtUt, self.statisticPrtIxt, self.statisticPrtUxt):
            idx = (self.statisticPrtUt, self.statisticPrtIxt, self.statisticPrtUxt).index(sender)
            self.statisticPrtTolerantValues[idx] = sender.value()
        self.updateStatisticPrtTable()

    def createStatisticPrtTable(self):
        self.statisticPrtTW = QTableWidget(len(mengy_spac800_bt.REPORT_PRT_CONFS), 3)
        self.statisticPrtTW.setVerticalHeaderLabels((i.name for i in mengy_spac800_bt.REPORT_PRT_CONFS))
        self.statisticPrtTW.setHorizontalHeaderLabels(("测量值", "标准值", "误差"))
        self.statisticPrtTW.resizeRowsToContents()
        header = self.statisticPrtTW.horizontalHeader()
        header.setSectionResizeMode(QHeaderView.Stretch)
        header = self.statisticPrtTW.verticalHeader()
        header.setSectionResizeMode(QHeaderView.Stretch)

        self.statisticPrtTW.setEditTriggers(QTableWidget.NoEditTriggers)
        for r in range(self.statisticPrtTW.rowCount()):
            for c in range(self.statisticPrtTW.columnCount()):
                self.statisticPrtTW.setItem(r, c, QTableWidgetItem(""))
                self.statisticPrtTW.item(r, c).setTextAlignment(Qt.AlignCenter)

    def updateStatisticPrtTable(self):
        """更新测试表格数据"""
        if any((key not in self.serRecvDir.keys() for key in range(0x000E, 0x001D + 1))):
            return
        # 保护监视部分
        for idx, conf in enumerate(mengy_spac800_bt.REPORT_PRT_CONFS):
            data = bytes2Int(self.serRecvDir.get(conf.dataAddress)) / conf.rate
            self.statisticPrtTW.item(idx, 0).setText("{:{}} {}".format(data, conf.format, conf.unit))
            if idx in (0, 1, 2, 3, 4, 5):
                sdata = self.statisticPrtUs.value() * self.prtGradeCombU.value()
                toleranceBase = self.statisticPrtTolerantValues[0]
            elif idx in (6, 8):
                sdata = self.statisticPrtIxs.value() * self.prtGradeCombIx.value()
                toleranceBase = self.statisticPrtTolerantValues[1]
            elif idx in (7, 9):
                sdata = self.statisticPrtUxs.value() * self.prtGradeCombUx.value()
                toleranceBase = self.statisticPrtTolerantValues[2]
            self.statisticPrtTW.item(idx, 1).setText("{:{}} {}".format(sdata, conf.format, conf.unit))
            if sdata == 0:
                sdata = 10 ** -5
            tolerance = (data - sdata) / sdata
            if tolerance <= 100:
                self.statisticPrtTW.item(idx, 2).setText("{:+.2%}".format(tolerance))
            else:
                self.statisticPrtTW.item(idx, 2).setText(">10000%")
            if abs(tolerance) * 100 <= toleranceBase:
                self.statisticPrtTW.item(idx, 2).setBackground(QColor("#00FF00"))
            else:
                self.statisticPrtTW.item(idx, 2).setBackground(QColor("#FF0000"))

    def initStaticsValue(self):
        self.statisticPrtUs.setValue(220)
        self.statisticPrtIxs.setValue(5)
        self.statisticPrtUxs.setValue(220)
        self.statisticMeaUs.setValue(220)
        self.statisticMeaIs.setValue(5)
        self.meaGradeCombRg.setValue(0)

    def dealKS833Output_Second(self, ks833_status):
        # self.powerLogWidget.appendPlainText("功率源输出状态 {}".format(ks833_status))
        # 电压标准值处理
        self.prtGradeCombU.setValue(ks833_status.Ua_Rms * (3 ** 0.5) / self.statisticPrtUs.value())
        self.prtGradeCombUx.setValue(ks833_status.Ua_Rms * (3 ** 0.5) / self.statisticPrtUxs.value())
        self.meaGradeCombU.setValue(ks833_status.Ua_Rms / self.statisticMeaUs.value())
        # 电流标准值
        self.prtGradeCombIx.setValue(ks833_status.Ia_Rms / self.statisticPrtIxs.value())
        self.meaGradeCombI.setValue(ks833_status.Ia_Rms / self.statisticMeaIs.value())
        # 角度
        self.meaGradeCombRg.setValue((360 - ks833_status.Ia_Ang) % 360)

    def creatrReportGenSubGroubBox(self):
        self.reportGenGroupBox = QGroupBox("测试数据处理")
        subLayout = QGridLayout(self.reportGenGroupBox)
        # self.reportClearBtn = QPushButton("清零")
        # self.reportClearBtn.clicked.connect(self.reportClearData)
        self.reportBrowseBtn = QPushButton("查看与输出")
        self.reportBrowser = ReportBrowser_BT(parent=self, serRecvDir=self.serRecvDir, sendQueuePack=self.sendQueuePack, logger=self.logger)
        self.reportBrowseBtn.clicked.connect(self.browseReport)
        subLayout.addWidget(self.reportBrowseBtn, 0, 0)
        # subLayout.addWidget(self.reportClearBtn, 1, 0)

    def createStatisticGroupBox(self):
        self.statisticGroupBox = QWidget()
        layout = QGridLayout(self.statisticGroupBox)

        layout.addWidget(self.prtBaseGroupBox, 0, 0, 5, 1)
        layout.addWidget(self.prtGradeGroupBox, 5, 0, 6, 1)
        layout.addWidget(self.prtTolerantGroupBox, 11, 0, 4, 1)
        layout.addWidget(self.reportGenGroupBox, 15, 0, 1, 1)

        layout.addWidget(self.statisticPrtTW, 0, 2, 11, 4)
        layout.addWidget(self.serialWidget, 11, 2, 3, 4)
        layout.addWidget(self.powerLogGroupBox, 14, 2, 2, 4)

        layout.addWidget(self.statisticMeaTW, 0, 6, 17, 6)
        layout.addWidget(self.meaBaseGroupBox, 0, 12, 3, 1)
        layout.addWidget(self.meaGradeFroupBox, 3, 12, 4, 1)
        layout.addWidget(self.meaTolerantGroupBox, 7, 12, 7, 1)
